package net.guerlab.smart.wx.service.service.impl;

import net.guerlab.commons.collection.CollectionUtil;
import net.guerlab.commons.number.NumberHelper;
import net.guerlab.smart.platform.server.service.BaseServiceImpl;
import net.guerlab.smart.wx.core.exception.AppIdInvalidException;
import net.guerlab.smart.wx.core.exception.OpenIdInvalidException;
import net.guerlab.smart.wx.core.searchparams.WxUserSearchParams;
import net.guerlab.smart.wx.service.entity.UserTagMapping;
import net.guerlab.smart.wx.service.entity.WxApp;
import net.guerlab.smart.wx.service.entity.WxUser;
import net.guerlab.smart.wx.service.mapper.WxUserMapper;
import net.guerlab.smart.wx.service.searchparams.UserTagMappingSearchParams;
import net.guerlab.smart.wx.service.service.AfterWxAppUpdateHandler;
import net.guerlab.smart.wx.service.service.AfterWxUserUpdateHandler;
import net.guerlab.smart.wx.service.service.UserTagMappingService;
import net.guerlab.smart.wx.service.service.WxUserService;
import net.guerlab.spring.commons.util.SpringApplicationContextUtil;
import net.guerlab.web.result.ListObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 微信用户服务实现
 *
 * @author guer
 */
@Service
public class WxUserServiceImpl extends BaseServiceImpl<WxUser, String, WxUserMapper>
        implements WxUserService, AfterWxAppUpdateHandler {

    private static final ThreadPoolExecutor EXECUTOR;

    private UserTagMappingService tagMappingService;

    static {
        int availableProcessors = Runtime.getRuntime().availableProcessors();
        EXECUTOR = new ThreadPoolExecutor(availableProcessors, availableProcessors * 2, 1L, TimeUnit.MINUTES,
                new LinkedBlockingQueue<>(), r -> {
            Thread thread = new Thread(r);
            thread.setName("wxUser updateBefore");
            return thread;
        }, new ScheduledThreadPoolExecutor.DiscardOldestPolicy());
    }

    @Override
    public ListObject<WxUser> queryPage(WxUserSearchParams searchParams) {
        if (refuseQuery(searchParams)) {
            return ListObject.empty();
        }
        return selectPage(searchParams);
    }

    @Override
    public Collection<WxUser> queryAll(WxUserSearchParams searchParams) {
        if (refuseQuery(searchParams)) {
            return Collections.emptyList();
        }
        return selectAll(searchParams);
    }

    private boolean refuseQuery(WxUserSearchParams searchParams) {
        if (NumberHelper.greaterZero(searchParams.getTagId()) || CollectionUtil.isNotEmpty(searchParams.getTagIds())) {
            UserTagMappingSearchParams userTagMappingSearchParams = new UserTagMappingSearchParams();
            userTagMappingSearchParams.setTagId(searchParams.getTagId());
            userTagMappingSearchParams.setTagIds(searchParams.getTagIds());

            List<String> openIds = CollectionUtil
                    .toList(tagMappingService.selectAll(userTagMappingSearchParams), UserTagMapping::getOpenId);

            if (openIds.isEmpty()) {
                return true;
            }

            searchParams.setOpenIds(openIds);
        }
        return false;
    }

    @Override
    public WxUser findUser(String appId, String openId) {
        if (StringUtils.isAnyBlank(appId, openId)) {
            return null;
        }

        WxUserSearchParams searchParams = new WxUserSearchParams();
        searchParams.setAppId(appId);
        searchParams.setOpenId(openId);

        return selectOne(searchParams);
    }

    @Override
    public void delete(String appId, String openId) {
        String trimAppId = StringUtils.trimToNull(appId);
        String trimOpenId = StringUtils.trimToNull(openId);

        if (trimAppId == null || trimOpenId == null) {
            return;
        }

        WxUserSearchParams searchParams = new WxUserSearchParams();
        searchParams.setAppId(trimAppId);
        searchParams.setOpenId(trimOpenId);

        mapper.deleteByExample(getExample(searchParams));
    }

    @Override
    protected void insertBefore(WxUser entity) {
        String appId = StringUtils.trimToNull(entity.getAppId());
        String openId = StringUtils.trimToNull(entity.getOpenId());

        if (appId == null) {
            throw new AppIdInvalidException();
        }
        if (openId == null) {
            throw new OpenIdInvalidException();
        }

        entity.setOpenId(openId);
        entity.setAppId(appId);
        entity.setUnionId(StringUtils.trimToEmpty(entity.getUnionId()));
        if (entity.getActivated() == null) {
            entity.setActivated(false);
        }
    }

    @Override
    protected void updateBefore(WxUser entity) {
        EXECUTOR.execute(
                () -> SpringApplicationContextUtil.getContext().getBeansOfType(AfterWxUserUpdateHandler.class).values()
                        .forEach(handler -> handler.afterWxUserUpdateHandler(entity)));
    }

    @Override
    public void afterWxAppUpdateHandler(WxApp wxApp) {
        String appId = wxApp.getAppId();
        String appName = StringUtils.trimToNull(wxApp.getAppName());

        if (appName == null) {
            return;
        }

        WxUser wxUser = new WxUser();
        wxUser.setAppName(appName);

        WxUserSearchParams searchParams = new WxUserSearchParams();
        searchParams.setAppId(appId);

        mapper.updateByExampleSelective(wxUser, getExample(searchParams));
    }

    @Autowired
    public void setTagMappingService(UserTagMappingService tagMappingService) {
        this.tagMappingService = tagMappingService;
    }
}
